/*!
 * name: Picker 1.0.3
 * author: chen guojun
 * other: Copyright 2021, MIT License
 */
;
(function (root, factory) {
  root = root || {};
  if (typeof module === "object" && module.exports) {
    module.exports = factory();
  } else {
    root.Picker = factory();
  }
})(this, function () {
  const ScrollPicker = function ScrollPicker(options = {}) {
    let opts = Object.assign({
      el: '',
      value: 0,
      list: [],
      itemHeight: 36,
      itemNum: 7,
      done: function (value) {}
    }, options);

    const Animate = {
      running: {}, // 运行
      counter: 1, // 计时器
      // 停止动画
      stop(id) {
        var cleared = Animate.running[id] !== null
        if (cleared) {
          Animate.running[id] = null
        }
        return cleared
      },
      // 判断给定的动画是否还在运行
      isRunning(id) {
        return Animate.running[id] !== null
      },
      start(stepCallback, verifyCallback, completedCallback, duration, easingMethod) {
        let desiredFrames = 60 // 每秒多少帧
        let millisecondsPerSecond = 1000 // 每秒的毫秒数
        let start = Date.now()
        let percent = 0 // 百分比
        let id = Animate.counter++
        let dropCounter = 0
        let step = function () {
          let now = Date.now()
          if (!Animate.running[id] || (verifyCallback && !verifyCallback(id))) {
            Animate.running[id] = null
            completedCallback && completedCallback(desiredFrames - (dropCounter / ((now - start) / millisecondsPerSecond)), id, false)
            return
          }

          if (duration) {
            percent = (now - start) / duration
            if (percent > 1) {
              percent = 1
            }
          }
          let value = easingMethod ? easingMethod(percent) : percent
          if (percent !== 1 && (!verifyCallback || verifyCallback(id))) {
            stepCallback(value)
            window.requestAnimationFrame(step)
          }
        }

        Animate.running[id] = true
        window.requestAnimationFrame(step)
        return id
      }
    }
    const createDom = ({
      el = 'div',
      attrs = {},
      html = '',
    }) => {
      let dom = document.createElement(el)
      Object.keys(attrs).forEach(item => {
        if (item === 'style' && Object.keys(item).length > 0) {
          let style = ''
          Object.keys(attrs.style).forEach(val => {
            style += `${val}:${attrs.style[val]};`
          })
          dom.setAttribute(item, style || '')
        } else {
          dom.setAttribute(item, attrs[item] || '')
        }
      })
      if (html !== '') dom.innerHTML = html
      return dom
    }

    let isMobile = function () {
      var navMatch = /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|JUC|WebOS|Windows Phone)/i;
      return navigator.userAgent.match(navMatch);
    };
    let isClick = true;
    const els = document.querySelector(opts.el) // 插件容器
    const wraper = createDom({
      el: "div",
      attrs: {
        class: 'picker-wraper',
        style: {
          height: opts.itemHeight * opts.itemNum + 'px',
        }
      }
    })
    const mask = createDom({
      el: "div",
      attrs: {
        class: 'picker-mask'
      }
    })
    const indicator = createDom({
      el: "div",
      attrs: {
        class: 'picker-indicator',
        style: {
          height: opts.itemHeight + 'px'
        }
      }
    })
    const content = createDom({
      el: "dl",
      attrs: {
        class: 'picker-content'
      }
    })
    
    wraper.appendChild(mask)
    wraper.appendChild(indicator)
    wraper.appendChild(content)
    els.appendChild(wraper)

    

    let itemHeight = opts.itemHeight
    let dragging = false
    let isAnimat = false // 是否开启动画
    let lastMove = 0 // 最后滚动时间记录
    let locList = [] // 记录位置和时间
    let deceleratMove = 0 // 减速运动速度
    let decelerat = false // 是否开启减速状态
    let currValue = 0 // 当前选中值
    let currIndex = 0 // 当前选中序列
    let startTop = 0
    let defScrollTop = 0
    let maxScrollTop = wraper.clientHeight / 2 // 滚动最大值
    let minScrollTop = 0 // 滚动最小值
    let medianValue = Math.round(maxScrollTop / itemHeight)
    let valueList = []

    const translate = (top) => {
      content.style.transform = 'translateY(' + Math.round(top) + 'px)'
    }

    const setTopValue = (top, duration) => {
      let numValue = top / itemHeight
      let currNum = Math.floor(Math.abs(numValue - medianValue))
      // 开始快后来慢的渐变曲线
      let easeOutCubic = (pos) => {
        return (Math.pow((pos - 1), 3) + 1)
      }
      // 以满足开始和结束的动画
      let easeInOutCubic = (pos) => {
        return ((pos /= 0.5) < 1 ? Math.pow(pos, 3) : (Math.pow((pos - 2), 3) + 2)) * 0.5
      }
      if (duration) {
        let wasAnimat = isAnimat
        let oldTop = defScrollTop
        let diffTop = top - oldTop
        let step = function (percent) {
          defScrollTop = oldTop + (diffTop * percent)
          translate(defScrollTop)
        }
        let verify = function (id) {
          return isAnimat === id
        }
        let completed = function (renderedFramesPerSecond, animationId, wasFinished) {
          if (animationId === isAnimat) {
            isAnimat = false
          }
        }
        isAnimat = Animate.start(step, verify, completed, duration, wasAnimat ? easeOutCubic : easeInOutCubic)
      } else {
        defScrollTop = top
        translate(top)
      }
      currValue = valueList[currNum - 1] || ''
      opts.done && opts.done(currValue)
    }
    const getValue = () => {
      return currValue
    }

    // 滚动到正确位置的方法
    let scrollToProperPlace = (top) => {
      top = Math.round((top / itemHeight).toFixed(5)) * itemHeight
      let newTop = Math.max(Math.min(maxScrollTop, top), minScrollTop)
      if (top !== newTop) {
        let h = itemHeight / 2
        top = newTop >= maxScrollTop ? newTop - h : newTop + h
      }
      setTopValue(top, 250)
    }

    // 开始减速动画
    const startDeceleration = () => {
      const step = () => {
        let scrollTop = defScrollTop + deceleratMove
        let scrollTopFixed = Math.max(Math.min(maxScrollTop, scrollTop), minScrollTop) // 不小于最小值，不大于最大值
        if (scrollTopFixed !== scrollTop) {
          scrollTop = scrollTopFixed
          deceleratMove = 0
        }
        if (Math.abs(deceleratMove) <= 1) {
          if (Math.abs(scrollTop % itemHeight) < 1) {
            deceleratMove = 0
          }
        } else {
          deceleratMove *= 0.95
        }
        setTopValue(scrollTop)
      }
      // 保持减速运行需要多少速度
      const verify = () => {
        return Math.abs(deceleratMove) >= 0.5
      }
      const completed = function () {
        decelerat = false
        if (defScrollTop <= minScrollTop || defScrollTop >= maxScrollTop) {
          scrollToProperPlace(defScrollTop)
          return
        }
      }
      decelerat = Animate.start(step, verify, completed)
    }
    const startHandler = (e) => {
      e = e || window.event;
      e.preventDefault()
      isClick = true;
      dragging = true
      startTop = e.touches ? e.touches[0].clientY : e.clientY
    }
    const moveHandler = (e) => {
      e = e || window.event;
      isClick = false;
      if (dragging && !isClick) {
        let currentTop = e.touches ? e.touches[0].clientY : e.clientY
        let scrollTop = defScrollTop
        scrollTop = scrollTop + (currentTop - startTop)
        if (scrollTop > maxScrollTop || scrollTop < minScrollTop) {
          scrollTop = scrollTop > maxScrollTop ? maxScrollTop : minScrollTop
        }
        if (locList.length > 40) {
          locList.splice(0, 20)
        }
        locList.push(scrollTop, e.timeStamp)
        startTop = currentTop
        lastMove = e.timeStamp
        setTopValue(scrollTop)
      }
    }
    const endHandler = (e) => {
      e = e || window.event;
      if (dragging && !isClick) {
        dragging = false
        if (e.timeStamp - lastMove < 100) { // 如果抬起时间和最后移动时间小于 100 证明快速滚动过
          // let positions = locList
          let endPos = locList.length - 1
          let startPos = endPos
          // 由于保存的时候位置跟时间都保存了， 所以 i -= 2
          for (let i = endPos; i > 0 && locList[i] > (lastMove - 100); i -= 2) {
            startPos = i
          }
          if (startPos !== endPos) {
            // 计算这两点之间的相对运动
            let timeOffset = locList[endPos] - locList[startPos] // 快速开始时间 - 结束滚动时间
            let movedTop = defScrollTop - locList[startPos - 1] // 最终距离 - 快速开始距离
            // 基于50ms计算每个渲染步骤的移动
            deceleratMove = movedTop / timeOffset * (1000 / 60) // 移动距离是用分钟来计算的
            // 开始减速的最小速度, 只有速度大于最小加速速度时才会出现下面的动画
            if (Math.abs(deceleratMove) > 4) {
              startDeceleration()
            }
          }
        }
        if (!decelerat) {
          scrollToProperPlace(defScrollTop)
        }
        locList.length = 0
      } else {
        dragging = false
      }
    }

    const wheel = (event) => {
      var delta = 0;
      if (!event) event = window.event;
      if (event.wheelDelta) { //IE、chrome浏览器使用的是wheelDelta，并且值为“正负120”
        delta = event.wheelDelta / 120;
        if (window.opera) delta = -delta; //因为IE、chrome等向下滚动是负值，FF是正值，为了处理一致性，在此取反处理
      } else if (event.detail) { //FF浏览器使用的是detail,其值为“正负3”
        delta = -event.detail / 3;
      }
      if (delta) {
        if (delta < 0) {
          //向下滚动
          const totalVal = (valueList.length * itemHeight) - ((medianValue - 1) * itemHeight) - itemHeight;
          if (Math.abs(defScrollTop) < totalVal) {
            defScrollTop = defScrollTop - itemHeight
          }
        } else {
          //向上滚动
          if (defScrollTop < (medianValue - 1) * itemHeight) {
            defScrollTop = defScrollTop + itemHeight
          }
        }
        setTopValue(defScrollTop)
      }
    }

    if (opts.list.length > 0) {
      opts.list.forEach((item,index) => {
        const dd = createDom({
          el: "dd",
          attrs: {
            'data-value': item.value || '',
            style: {
              height: opts.itemHeight + 'px',
              'line-height': opts.itemHeight + 'px',
            }
          },
          html: item.title || ''
        })
        dd.addEventListener('click',()=>{
          if(isClick) {
            currIndex = index+1;
            const contrast = (currIndex < medianValue || currIndex > medianValue)
            setTopValue(contrast ? ((medianValue - currIndex) * itemHeight) : 0)
          }
        })
        content.appendChild(dd)
      })
    }
    minScrollTop = -(content.offsetHeight - maxScrollTop);
    indicator.style.top = maxScrollTop - (itemHeight / 2) + 'px';
    Array.prototype.slice.call(content.children).forEach((el, index) => {
      let value = parseInt(el.getAttribute('data-value'))
      if (value === opts.value) {
        currIndex = index + 1
      }
      valueList.push(value)
    })

    const contrast = (currIndex < medianValue || currIndex > medianValue)
    setTopValue(contrast ? ((medianValue - currIndex) * itemHeight) : 0)

    wraper.addEventListener('mousewheel', wheel)
    wraper.addEventListener('DOMMouseScroll', wheel)
    wraper.addEventListener(isMobile() ? 'touchstart' : 'mousedown', startHandler)
    document.addEventListener(isMobile() ? 'touchmove' : 'mousemove', moveHandler)
    document.addEventListener(isMobile() ? 'touchend' : 'mouseup', endHandler)

    return {
      getValue
    }
  }

  const Picker = function Picker(elem, params) {
    return new ScrollPicker(elem, params)
  }
  return Picker;
});
